package com.java.nio.groupchat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * @author jl
 * Created on 2020/9/27
 * 群聊服务器端
 * 1.启动服务器监听6667端口
 * 2.服务器接收客户端消息,并实现转发(处理上线和离线)
 */
public class GroupChatServer {
    // 定义属性
    private ServerSocketChannel listenChannel;

    private Selector selector;

    private static final int PORT = 6667;

    // 初始化
    public GroupChatServer() {
        try {
            this.listenChannel = ServerSocketChannel.open();
            this.selector = Selector.open();
            this.listenChannel.socket().bind(new InetSocketAddress(PORT));
            this.listenChannel.configureBlocking(false);
            this.listenChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 监听
    private void listen() {
        System.out.println("监听线程: " + Thread.currentThread().getName());

        // 循环监听
        while (true) {
            try {
                // selector.select()返回有事件发生的通道的个数
                int count = selector.select(2000);
                // 接收到事件
                if (count > 0) {
                    // 获取SelectionKey的集合
                    Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
                    while (keyIterator.hasNext()) {
                        SelectionKey key = keyIterator.next();

                        if (key.isAcceptable()) { // 客户端连接事件
                            // 给客户端生成socketChannel,并注册到selector,监听OP_READ
                            SocketChannel sc = listenChannel.accept();
                            sc.configureBlocking(false);
                            sc.register(selector, SelectionKey.OP_READ);
                            System.out.println("客户端[" + sc.getRemoteAddress().toString().substring(1) + "]上线");
                        }

                        if (key.isReadable()) { // 读事件
                            readMessage(key);
                        }
                        //当前的key删除，防止重复处理
                        keyIterator.remove();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void readMessage(SelectionKey key) {
        // 取到关联的channel
        SocketChannel channel = null;
        try {
            // 通过SelectionKey获取对应的channel
            channel = (SocketChannel) key.channel();
            // 创建buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            // 从channel中读取数据到buffer
            int read = channel.read(buffer);

            if (read > 0) {
                // 把缓存区的数据转成字符串
                String msg = new String(buffer.array());
                // 输出该消息
                System.out.println("form 客户端: " + msg);
                // 向其它的客户端转发消息(去掉自己)
                sendMessageToOtherChannel(msg, channel);
            }
        } catch (IOException e) {
            // key.channel()获取通道失败
            try {
                System.out.println(channel.getRemoteAddress() + " 离线了..");
                // 取消注册
                key.cancel();
                // 关闭通道
                channel.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

    // 转发消息给其他通道
    private void sendMessageToOtherChannel(String msg, SocketChannel self) throws IOException {
        // 遍历所有注册到selector上的SocketChannel,并排除 self
        for (SelectionKey key : selector.keys()) {
            // 通过SelectionKey获取对应的channel
            Channel channel = key.channel();
            // 排除消息来源的客户端
            if (channel instanceof SocketChannel && channel != self) {
                SocketChannel sc = (SocketChannel) channel;
                //将msg存储到buffer
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                //将buffer的数据写入通道
                sc.write(buffer);
            }
        }
    }

    public static void main(String[] args) {
        GroupChatServer groupChatServer = new GroupChatServer();
        // 单线程单Reactor 服务器端和客户端是同一个selector
        System.out.println(groupChatServer.selector);
        groupChatServer.listen();
    }


}
